using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Threading;
using PandoraCommon;

namespace TrackVision
{
    /// <summary>
    /// Contains the information of a specific position.
    /// </summary>
    public class TrackDataItem
    {

        /// <summary>
        /// Initializes a new instance of the <see cref="TrackDataItem"/> class.
        /// </summary>
        public TrackDataItem()
        {
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="TrackDataItem"/> class.
        /// </summary>
        /// <param name="gpsPosition">The GPS position.</param>
        /// <param name="secondsElapsed">The seconds elapsed.</param>
        /// <param name="distanceTravelled">The distance travelled.</param>
        public TrackDataItem(GpsPosition gpsPosition, double centerLatitude, double centerLongitude, 
                            double secondsElapsed, double distanceTraveled)
        {           
            this.acceleration = gpsPosition.Acceleration;
            this.lateralAcceleration = gpsPosition.LateralAcceleration;
            this.distance = distanceTraveled;
            this.isAccelerating = gpsPosition.IsAccelerating;
            this.positionX = processLatitude(gpsPosition.Latitude, centerLatitude);
            this.positionY = processLongitude(gpsPosition.Longitude, centerLongitude); 
            this.speed = gpsPosition.Speed;
            this.time = Math.Round(secondsElapsed,1);
            this.heading = gpsPosition.Heading;
            this.latitude = gpsPosition.Latitude;
            this.longitude = gpsPosition.Longitude;
            this.altitude = gpsPosition.Altitude;
            this.dateTime = gpsPosition.Date;
        }

        #region Time
        private double time;

        /// <summary>
        /// Gets or sets the time, in seconds, elapsed since the previous position.
        /// </summary>
        /// <value>Time [s]</value>
        public double Time
        {
            get { return time; }
            set { time = value; }
        } 
        #endregion

        #region Acceleration
        private double acceleration;

        /// <summary>
        /// Gets or sets the longitudinal acceleration (in G = 10m/s2).
        /// </summary>
        /// <value>Accel Longitudinal [g]</value>
        public double Acceleration
        {
            get { return acceleration; }
            set { acceleration = value; }
        } 
        #endregion

        #region LateralAcceleration
        private double lateralAcceleration;

        /// <summary>
        /// Gets or sets the lateral acceleration (in G = 10m/s2).
        /// </summary>
        /// <value>Accel Lateral [g]</value>
        public double LateralAcceleration
        {
            get { return lateralAcceleration; }
            set { lateralAcceleration = value; }
        }
        #endregion

        #region IsAccelerating
        private bool isAccelerating;

        /// <summary>
        /// Gets or sets a value indicating whether the vehicle speed is increasing.
        /// </summary>
        /// <value>
        ///     Accel/Brake Flag [-]
        /// 	<c>true</c> if the vehicle is accelerating; otherwise, <c>false</c>.
        /// </value>
        public bool IsAccelerating
        {
            get { return isAccelerating; }
            set { isAccelerating = value; }
        } 
        #endregion

        #region Speed
        private double speed;

        /// <summary>
        /// Gets or sets the speed of the vehicle, in miles per hour.
        /// </summary>
        /// <value>GPS Speed [mph]</value>
        public double Speed
        {
            get { return speed; }
            set { speed = value; }
        } 
        #endregion

        #region Distance
        private double distance;

        /// <summary>
        /// Gets or sets the distance traveled, in miles.
        /// </summary>
        /// <value>Distance [miles]</value>
        public double Distance
        {
            get { return distance; }
            set { distance = value; }
        } 
        #endregion

        #region PositionX
        private double positionX;

        /// <summary>
        /// Gets or sets the X coordinate of the current position, in meters.
        /// </summary>
        /// <value>Position X [m]</value>
        public double PositionX
        {
            get { return positionX; }
            set { positionX = value; }
        } 
        #endregion

        #region PositionY
        private double positionY;

        /// <summary>
        /// Gets or sets the Y coordinate of the current position, in meters.
        /// </summary>
        /// <value>Position Y [m]</value>
        public double PositionY
        {
            get { return positionY; }
            set { positionY = value; }
        }
        #endregion

        #region Latitude
        private double latitude = 0;

        /// <summary>
        /// Gets or sets the latitude.
        /// </summary>
        /// <value>The latitude.</value>
        public double Latitude
        {
            get { return latitude; }
            set { latitude = value; }
        }
        #endregion

        #region Longitude
        private double longitude = 0;

        /// <summary>
        /// Gets or sets the longitude.
        /// </summary>
        /// <value>The longitude.</value>
        public double Longitude
        {
            get { return longitude; }
            set { longitude = value; }
        }
        #endregion

        #region Altitude
        private double altitude = 0;

        /// <summary>
        /// Gets or sets the altitude.
        /// </summary>
        /// <value>The altitude.</value>
        public double Altitude
        {
            get { return altitude; }
            set { altitude = value; }
        }
        #endregion

        #region Heading
        private double heading = 0;

        /// <summary>
        /// Gets or sets the heading.
        /// </summary>
        /// <value>The heading.</value>
        public double Heading
        {
            get { return heading; }
            set { heading = value; }
        }
        #endregion

        private DateTime dateTime;

        public DateTime DateTime
        {
            get { return dateTime; }
            set { dateTime = value; }
        }


        private double processLatitude(double latitude, double latitudeCenter)
        {
            //double degrees = Math.Truncate(latitude);
            //double minutes = Math.Truncate((latitude - degrees) * 60);
            //double seconds = (latitude - (minutes / 60));

            return Math.Round((latitude - latitudeCenter) * 3600 * 30.9);
        }

        private double processLongitude(double longitude, double longitudeCenter)
        {
            //double degrees = Math.Truncate(longitude);
            //double minutes = Math.Truncate((longitude - degrees) * 60);
            //double seconds = (longitude - (minutes / 60));

            return Math.Round((longitude - longitudeCenter) * 3600 * 30.9);
        }

        /// <summary>
        /// Returns a TrackVision CSV formatted <see cref="T:System.String"></see> that represents the current <see cref="TrackDataItem"></see>.
        /// This "old" method would require the logger to be chosen in TrackVision to be "Race Technology Analysis version 4KPH".
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the previous position's <see cref="TrackDataItem"></see>. 
        /// Use null if this is the first position.
        /// </returns>
        public string ToTrackVisionOld()
        {
            CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-us");

            StringBuilder sb = new StringBuilder();
            try
            {
                //Time [s]
                sb.Append(time.ToString());
                sb.Append(",");

                //Accel Longitudinal [g]
                sb.Append(Math.Round(acceleration / 9.80665, 2).ToString()); // convert m/s2 to g
                sb.Append(",");

                // Accel/Brake Flag [-]
                if (isAccelerating)
                    sb.Append("1,");
                else
                    sb.Append("0,");

                //Accel Lateral [g], Accel Total [g], Engine Speed [rpm], 
                sb.Append("0,0,0,"); // Not available

                // GPS Speed [mph]
                sb.Append(Math.Round(speed / 1.60934721869).ToString());
                sb.Append(",");

                // GPS Speed [kph]
                sb.Append(Math.Round((speed)).ToString());
                sb.Append(",");

                // Wheel Speed [mph]
                sb.Append(Math.Round(speed / 1.60934721869).ToString());
                sb.Append(",");

                // Wheel Speed [kph]
                sb.Append(Math.Round((speed)).ToString());
                sb.Append(",");

                // Distance [km]
                sb.Append((distance * 1000).ToString());
                sb.Append(",");

                // Distance [miles]
                sb.Append((distance * 1609.3472186943067).ToString());
                sb.Append(",");

                // Distance [m]
                sb.Append(distance.ToString());
                sb.Append(",");

                // Distance [ft]
                sb.Append((distance * 0.30480000000029017).ToString());

                // Power [hp]
                sb.Append(",0,"); // Not available

                // Position X [m]
                sb.Append(positionX.ToString());
                sb.Append(",");

                // Position Y [m]
                sb.Append(positionY.ToString());

                // Time Slip [s], Rate of Time Slip [%], Track Side Beacon [-], Sat Count [-]
                sb.Append(",0,0,0,0");// Not available               
            }
            catch
            {
                throw;
            }
            finally
            {
                Thread.CurrentThread.CurrentCulture = currentCulture;
            }

            return sb.ToString();
        }

        /// <summary>
        /// Returns a TrackVision CSV formatted <see cref="T:System.String"></see> that represents the current <see cref="TrackDataItem"></see>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the previous position's <see cref="TrackDataItem"></see>. 
        /// Use null if this is the first position.
        /// </returns>
        public string ToTrackVision()
        {
            CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-us");

            StringBuilder sb = new StringBuilder();
            try
            {
                //TIME
                sb.Append(time.ToString());
                sb.Append(",");

                // X
                sb.Append(positionX.ToString());
                sb.Append(",");

                // Y
                sb.Append(positionY.ToString());
                sb.Append(",");

                switch (WriterSettings.GetInstance().SpeedUnits)
                {
                    case SpeedUnits.KPH:
                        // SPEED_KPH
                        sb.Append(Math.Round(speed).ToString());
                        sb.Append(",");

                        // SPEED_MPH
                        sb.Append(0);
                        sb.Append(",");
                        break;
                    case SpeedUnits.MPH:
                        // SPEED_KPH
                        sb.Append(0);
                        sb.Append(",");

                        // SPEED_MPH
                        sb.Append(Math.Round(speed / 1.60934721869).ToString());
                        sb.Append(",");
                        break;
                    case SpeedUnits.Knots:
                        // SPEED_KPH (KPH column in TrackVision CSV field, but values written are nautical Knots!)
                        sb.Append(Math.Round(speed / 1.852,1));
                        sb.Append(",");

                        // SPEED_MPH (MPH column in TrackVision CSV field, but values written are nautical Knots!)
                        sb.Append(Math.Round(speed / 1.852,1));
                        sb.Append(",");
                        break;
                    default:
                        break;
                }

                // LONGITUDINAL ACCELERATION (G)
                sb.Append(Math.Round(acceleration / 9.80665, 2).ToString()); // convert m/s2 to g
                sb.Append(",");

                // LATERAL ACCELERATION (G)
                // NOT WORKING -> sb.Append(Math.Round(lateralAcceleration / 9.80665, 2).ToString()); // convert m/s2 to g
                //sb.Append(Math.Round(lateralAcceleration, 2).ToString()); // DEBUG
                sb.Append(Math.Round(lateralAcceleration, 2).ToString()); // DEBUG
                sb.Append("0,");

                // DISTANCE_KM
                sb.Append(Math.Round(distance, 3).ToString());
                sb.Append(",");

                // DISTANCE_MILES
                sb.Append(Math.Round(distance / 1.6093472186943067,3).ToString());
                sb.Append(",");

                // DISTANCE_FEET
                sb.Append(Math.Round(distance / 1.6093472186943067 * 5280).ToString());
                sb.Append(",");

                // IS_BREAKING
                if (isAccelerating)
                    sb.Append("1,");
                else
                    sb.Append("0,");


                //not_used = "LATITUDE";
                sb.Append(this.latitude.ToString());
                sb.Append(",");

                //not_used = "LONGITUDE";
                sb.Append(this.longitude.ToString());
                sb.Append(",");

                //not_used = "ALTITUDE_METERS";
                sb.Append(Math.Round(this.altitude).ToString());
                sb.Append(",");

                //not_used = "ALTITUDE_FEET";
                sb.Append(Math.Round(this.altitude * 3.2808399).ToString());
                sb.Append(",");

                //not_used = "HEADING";
                sb.Append(Math.Round(this.heading,1).ToString());
                sb.Append(",");

                //not_used = "DATE";
                sb.Append(this.dateTime.ToString("dd/MM/yyyy"));

                //not_used = "TIME";
                sb.Append(this.dateTime.ToString("hh:mm:ss.fff"));
             
            }
            catch
            {
                throw;
            }
            finally
            {
                Thread.CurrentThread.CurrentCulture = currentCulture;
            }

            return sb.ToString();
        }
    }
}
